
#include <BOpenGL>
#include <BRender>
#include "IAlgorithm.h"
#include "member_BGeometry.h"
#include "member_BElement.h"

using namespace BWE;

member_BGeometry::member_BGeometry(BGeometry* geometry)
{
	boss = geometry;
	dirtyTree = true;
}
member_BGeometry::~member_BGeometry()
{

}

void member_BGeometry::drawPiecesLObject(BElement* element, int mode)
{
	if (element_member(element)->dirtyList)
	{
		BPieceArray* pieces = element->pieces();
		BColorArray* cs = colors;
		BVectorArray* vs = vertices;
		BVectorArray* ns = normals;
		BCoordArray* ts = coords.at(0);
		if (element_member(element)->listName == 0)
		{
			GLuint listName = glGenLists(1);
			element_member(element)->listName = listName;
		}
		GLuint listName = element_member(element)->listName;
		glNewList(listName, GL_COMPILE);
		glBegin(mode);
		for (int i = 0; i < pieces->size(); i++)
		{
			const BPiece& piece = (*pieces)[i];
			if (cs)
			{
				const BColor& color = (*cs)[piece.v()];
				glColor4ubv((GLubyte*)&color);
			}
			if (ts)
			{
				const BCoord& coord = (*ts)[piece.vt()];
				glTexCoord2fv((GLfloat*)&coord);
			}
			if (ns)
			{
				const BVector& normal = (*ns)[piece.vn()];
				BOpenGL::glNormal(normal.x(), normal.y(), normal.z());
			}
			const BVector& vertex = (*vs)[piece.v()];
			BOpenGL::glVertex(vertex.x(), vertex.y(), vertex.z());
		}
		glEnd();
		glEndList();
		element_member(element)->dirtyList = false;
	}
	GLuint listName = element_member(element)->listName;
	if (listName > 0)
	{
		glCallList(listName);
	}
}

BSpace member_BGeometry::triangleSpace(const BTriangle& triangle) const
{
	BSpace space(triangle.a());
	space.expand(triangle.b());
	space.expand(triangle.c());
	return space;
}
BSpace member_BGeometry::quadSpace(const BQuad& quad) const
{
	BSpace space(quad.a());
	space.expand(quad.b());
	space.expand(quad.c());
	space.expand(quad.d());
	return space;
}
BSpace member_BGeometry::polygonSpace(const BPolygon& polygon) const
{
	if (polygon.empty())
		return BSpace();
	BSpace s(polygon[0]);
	for (int i = 1; i < polygon.size(); i++)
	{
		s.expand(polygon[i]);
	}
	return s;
}

void member_BGeometry::freshPolyTree()
{
	BSpace treeSpace = polyOctTree->space();
	const BSpace& space = boss->space();
	if (polyOctTree && treeSpace.contain(space))
		return;

	polyOctTree->reset(space);
	for (int i = 0; i < elements.size(); i++)
	{
		const BElement* element = elements[i];
		const BPieceArray* pieces = element->pieces();
		if (pieces)
		{
			int size = pieces->size();
			if (element->primitive() == Primitive_Triangles)
			{
				for (int i = 0; i < size; i += 3)
				{
					int ia = (*pieces)[i].v();
					int ib = (*pieces)[i + 1].v();
					int ic = (*pieces)[i + 2].v();
					BTriangle triangle((*vertices)[ia], (*vertices)[ib], (*vertices)[ic]);
					auto item = polyOctTree->create(triangleSpace(triangle));
					item->triangles.append(triangle);
				}
				continue;
			}
			if (element->primitive() == Primitive_Quads)
			{
				for (int i = 0; i < size; i += 4)
				{
					int ia = (*pieces)[i].v();
					int ib = (*pieces)[i + 1].v();
					int ic = (*pieces)[i + 2].v();
					int id = (*pieces)[i + 3].v();
					BQuad quad((*vertices)[ia], (*vertices)[ib], (*vertices)[ic], (*vertices)[id]);
					auto item = polyOctTree->create(quadSpace(quad));
					item->quads.append(quad);
				}
				continue;
			}
			continue;
		}
		const BIntArray* indices = element->indices();
		if (indices)
		{
			int size = indices->size();
			if (element->primitive() == Primitive_Triangles)
			{
				for (int i = 0; i < size; i += 3)
				{
					int ia = (*indices)[i];
					int ib = (*indices)[i + 1];
					int ic = (*indices)[i + 2];
					BTriangle triangle((*vertices)[ia], (*vertices)[ib], (*vertices)[ic]);
					auto item = polyOctTree->create(triangleSpace(triangle));
					item->triangles.append(triangle);
				}
			}
			if (element->primitive() == Primitive_Quads)
			{
				for (int i = 0; i < size; i += 4)
				{
					int ia = (*indices)[i];
					int ib = (*indices)[i + 1];
					int ic = (*indices)[i + 2];
					int id = (*indices)[i + 3];
					BQuad quad((*vertices)[ia], (*vertices)[ib], (*vertices)[ic], (*vertices)[id]);
					auto item = polyOctTree->create(quadSpace(quad));
					item->quads.append(quad);
				}
			}
			continue;
		}
		int first = element->first();
		int count = element->count();
		if (element->primitive() == Primitive_Triangles)
		{
			for (int c = 0; c < count; c += 3)
			{
				int ia = first + c;
				int ib = ia + 1;
				int ic = ia + 2;
				BTriangle triangle((*vertices)[ia], (*vertices)[ib], (*vertices)[ic]);
				auto item = polyOctTree->create(triangleSpace(triangle));
				item->triangles.append(triangle);
			}
		}
		if (element->primitive() == Primitive_Quads)
		{
			for (int c = 0; c < count; c += 3)
			{
				int ia = first + c;
				int ib = ia + 1;
				int ic = ib + 1;
				int id = ic + 1;
				BQuad quad((*vertices)[ia], (*vertices)[ib], (*vertices)[ic], (*vertices)[id]);
				auto item = polyOctTree->create(quadSpace(quad));
				item->quads.append(quad);
			}
		}
		if (element->primitive() == Primitive_Polygon)
		{
			BPolygon polygon;
			polygon.set(vertices->data() + first, count);
			auto item = polyOctTree->create(polygonSpace(polygon));
			item->polygons.append(polygon);
		}

	}
}
void member_BGeometry::freshElementBoxes()
{
	for (int i = 0; i < elements.size(); i++)
	{
		BElement* element = elements[i];
		if (const BIntArray* indices = element->indices())
		{
			int first = indices->first();
			BSpace space = vertices->at(first);
			for (int i = 1; i < indices->size(); i++)
			{
				int index = (*indices)[i];
				space.expand(vertices->at(index));
			}
			element_member(element)->box = space;
		}
		else if (const BPieceArray* pieces = element->pieces())
		{
			int first = pieces->first().v();
			BSpace space = vertices->at(first);
			for (int i = 1; i < pieces->size(); i++)
			{
				const BPiece& piece = (*pieces)[i];
				space.expand(vertices->at(piece.v()));
			}
			element_member(element)->box = space;
			element_member(element)->dirtyList = true;
		}
		else if (int count = element->count())
		{
			int first = element->first();
			BSpace space = vertices->at(first);
			for (int i = 1; i < count; i++)
			{
				int index = first + i;
				space.expand(vertices->at(index));
			}
			element_member(element)->box = space;
		}
	}
}

void member_BGeometry::buildTree(int startIndex, int endIndex)
{
	Node& node = treeNodes[lastIndex];
	int numIndices = endIndex - startIndex;
	if (numIndices == 1)
	{
		node = leafNodes[startIndex];
		lastIndex++;
		return;
	}

	BVector means;
	for (int i = startIndex; i < endIndex; i++)
	{
		means += leafNodes[i].center;
	}
	means /= numIndices;

	BVector variance;
	for (int i = startIndex; i < endIndex; i++)
	{
		BVector diff2 = leafNodes[i].center - means;
		variance += diff2 * diff2;
	}
	variance /= numIndices - 1;

	int splitIndex = startIndex;
	{
		int axis = MaxAxis(variance);
		BReal splitValue = means[axis];
		for (int i = startIndex; i < endIndex; i++)
		{
			const BVector& center = leafNodes[i].center;
			if (center[axis] > splitValue)
			{
				leafNodes.swap(i, splitIndex);
				splitIndex++;
			}
		}
		int numBalanced = numIndices / 3;
		if (splitIndex <= (startIndex + numBalanced) || splitIndex >= (endIndex - 1 - numBalanced))
		{
			splitIndex = startIndex + (numIndices >> 1);
		}
	}

	node.space.min() = localSpace.max();
	node.space.max() = localSpace.min();
	for (int i = startIndex; i < endIndex; i++)
	{
		const BSpace& space = leafNodes[i].space;
		node.space.expand(space);
	}

	int curIndex = lastIndex++;
	buildTree(startIndex, splitIndex);
	buildTree(splitIndex, endIndex);
	node.index = curIndex - lastIndex;
}
void member_BGeometry::overlapping(ITriangleCallback* callback, const BSpace& space)
{
	if (dirtyTree)
	{
		for (int i = 0; i < elements.size(); i++)
		{
			BElement* element = elements[i];
			int first = element->first();
			int count = element->count();
			if (BIntArray* indices = element->indices())
			{
				if (indices->empty())
					continue;
				if (element->primitive() == Primitive_Triangles)
				{
					for (int i = 0; i < count; i += 3)
					{
						int ia = indices->at(first + i + 0);
						int ib = indices->at(first + i + 1);
						int ic = indices->at(first + i + 2);
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						Node& node = leafNodes.append();
						node.space.reset(va);
						node.space.expand(vb);
						node.space.expand(vc);
						node.center = node.space.center();
						node.index = i;
						node.ia = ia;
						node.ib = ib;
						node.ic = ic;
					}
				}
				if (element->primitive() == Primitive_Quads)
				{
					for (int i = 0; i < count; i += 4)
					{
						int ia = indices->at(first + i + 0);
						int ib = indices->at(first + i + 1);
						int ic = indices->at(first + i + 2);
						int id = indices->at(first + i + 3);
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						const BVector& vd = (*vertices)[id];
						Node& node = leafNodes.append();
						node.space.reset(va);
						node.space.expand(vb);
						node.space.expand(vc);
						node.space.expand(vd);
						node.center = node.space.center();
						node.index = i;
						node.ia = ia;
						node.ib = ib;
						node.ic = ic;
						node.id = id;
					}
				}
			}
			else
			{
				if (element->primitive() == Primitive_Triangles)
				{
					for (int i = 0; i < count; i += 3)
					{
						int ia = first + i;
						int ib = first + i + 1;
						int ic = first + i + 2;
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						Node& node = leafNodes.append();
						node.space.reset(va);
						node.space.expand(vb);
						node.space.expand(vc);
						node.center = node.space.center();
						node.index = i;
						node.ia = ia;
						node.ib = ib;
						node.ic = ic;
					}
				}
				if (element->primitive() == Primitive_Quads)
				{
					for (int i = 0; i < count; i += 4)
					{
						int ia = first + i;
						int ib = first + i + 1;
						int ic = first + i + 2;
						int id = first + i + 3;
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						const BVector& vd = (*vertices)[id];
						Node& node = leafNodes.append();
						node.space.reset(va);
						node.space.expand(vb);
						node.space.expand(vc);
						node.space.expand(vd);
						node.center = node.space.center();
						node.index = i;
						node.ia = ia;
						node.ib = ib;
						node.ic = ic;
						node.id = id;
					}
				}
			}
		}
		if (leafNodes.size())
		{
			lastIndex = 0;
			treeNodes.resize(2 * leafNodes.size());
			buildTree(0, leafNodes.size());
		}
		leafNodes.clear();
		dirtyTree = false;
	}
	int index = 0;
	while (index < lastIndex)
	{
		const Node& node = treeNodes[index];
		bool overlapped = space.intersect(node.space);
		if (overlapped && node.index >= 0)
		{
			processNode(callback, node);
		}
		if (overlapped || node.index >= 0)
		{
			index++;
		}
		else
		{
			index -= node.index;
		}
	}
}
void member_BGeometry::overlapping(ITriangleCallback* callback, const BLine& line)
{
	int index = 0;
	while (index < lastIndex)
	{
		const Node& node = treeNodes[index];
		bool overlapped = line.intersect(node.space);
		if (overlapped && node.index >= 0)
		{
			processNode(callback, node);
		}
		if (overlapped || node.index >= 0)
		{
			index++;
		}
		else
		{
			index -= node.index;
		}
	}
}
void member_BGeometry::processNode(ITriangleCallback* callback, const Node& node)
{
	const BVectorArray* vertices = boss->vertices();
	BTriangle triangle;
	triangle[0] = vertices->at(node.ia);
	triangle[1] = vertices->at(node.ib);
	triangle[2] = vertices->at(node.ic);
	callback->process(triangle, 0);
	if (node.id >= 0)
	{
		triangle[0] = vertices->at(node.ia);
		triangle[1] = vertices->at(node.ic);
		triangle[2] = vertices->at(node.id);
		callback->process(triangle, 0);
	}
}
